﻿// RootRpcServer.cpp : Implements the server-side basic RPC functions
//

#include "pch.h"
#include "RootRpcServer.h"

PCWSTR RPC_SDDL = L"D:"
	L"(A;;GA;;;" ID_CAP_ROOTRPC_APP_STRING L")"	// Allow the ROOTRPC app capability
	L"(A;;GA;;;S-1-5-21-2702878673-795188819-444038987-1030)"	// Allow apps at all
	L"(A;;GA;;;" ID_CAP_ROOTRPC_SVC_STRING L")"	// Allow the ROOTRPC service capability
	L"(A;;0x111fffff;;;CO)"						// ALlow the creator/owner
	L"(A;;0x111fffff;;;SY)"						// Allow the SYSTEM account
	L"(A;;0x111fffff;;;BA)";					// Allow the built-in Administrators group

volatile DWORD clientCount = 0;

WINERR StartRpcServer()
{
	RPC_STATUS status;
	PSECURITY_DESCRIPTOR psd;
#ifdef QUERYING_SECURITY_DESCRIPTOR
	NTSTATUS nstat;
#else
	ULONG psdsize = 0x0;
#endif

	LogVerbose(L"Starting the RPC listener...\n");
	// First, get the security descriptor.
#ifdef QUERYING_SECURITY_DESCRIPTOR
	// For the moment, use one that allows access by any app.
	// TODO: Tighten this down to one specifically for root-using apps!
	nstat = ::QueryTransientObjectSecurityDescriptor(TRANSIENT_RPC, L"DefaultInterface", &psd);
	if (nstat)
	{
		// How bad is it?
		if (SUCCEEDED(nstat))
		{
			// OK, non-zero value but not a failure. Interesting...
			LogWarning(
				L"QueryTransientObjectSecurityDescriptor returned unexpected NTSTATUS 0x%X!\n",
				nstat);
		}
		else
		{
			// Something went very wrong. We should try to figure out what.
			LogError(
				L"QueryTransientObjectSecurityDescriptor failed with NTSTATUS 0x%X! Exiting.\n",
				nstat);
			return nstat;
		}
	}
	else
	{
		LogDebug(L"QueryTransientObjectSecurityDescriptor succeeded.\n");
	}
#else
	if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(
		RPC_SDDL,
		SDDL_REVISION_1,
		&psd,
		&psdsize))
	{
		status = ::GetLastError();
		LogError(
			L"ConvertStringSecurityDescriptorToSecurityDescriptorW failed, Win32 error %d!\n",
			status);
		return status;
	}
#endif
	// OK, got the security descriptor.
	// Now, register the protseqs and endpoints in the IDL
	// Note that we specify the security descriptor, even though MSDN suggests don't for endpoints
	// If we don't specify it, apps running in AppContainers will not be able to connect
	status = ::RpcServerUseAllProtseqsIf(
		RPC_C_PROTSEQ_MAX_REQS_DEFAULT,		// Don't care about queue for TCP (using NCALRPC)
		RootRpcInterface_v0_3_s_ifspec,		// Interface handle for the server
		psd);								// Use the retrieved/converted security descriptor
	if (status)
	{
		// Something about the protseq/ep in the IDL file is wrong?
		LogError(L"RpcServerUseAllProtseqsIf failed with status %d! Exiting.\n", status);
		return status;
	}
	else
	{
		LogDebug(L"RpcServerUseAllProtseqsIf succeeded.\n");
	}
	// All right, we've registered the protseq(s) and endpoint(s).
	// Time to register the interface.
	// Could use auto-listening, but that makes it awkward to wait until the server finishes.
	// Use RpcServerRegisterIf3 because it lets us specify the SD for the interface.
	// Without that, AppContainer clients cannot call functions in the interface.
	// See remarks https://msdn.microsoft.com/en-us/library/windows/desktop/jj552974(v=vs.85).aspx
	LogDebug(
		L"RpcServerRegisterIf3(RootRpcInterface_v0_3, null, null, 0, MAX_CALLS_DEFAULT, "
		L"-1, SecurityCallback, psd)\n");
	status = ::RpcServerRegisterIf3(
		RootRpcInterface_v0_3_s_ifspec,		// Server-side binding handle
		nullptr,							// Don't specify a UUID for the RPC manager EPV
		nullptr,							// Use the MIDL-generated entry point vector
		0x0,								// No flags
	//	RPC_IF_AUTOLISTEN,					// Not using auto-listening; makes waiting awkward
		RPC_C_LISTEN_MAX_CALLS_DEFAULT,		// Not worried the about number of concurrent calls
		0xFFFFFFFF,							// Irrelevant for local RPC (ncalrpc)
		SecurityCallback,					// Callback function (currently permits everything)
		psd);								// Use the security descriptor that we queried
	if (status)
	{
		// Unable to register the interface for some reason
		LogError(L"RpcServerRegisterIf3 failed with status %d!\n", status);
		return status;
	}
	else
	{
		LogDebug(L"RpcServerRegisterIf3 succeeded.\n");
	}
	// OK, the server is registered (but not listening yet).
	// Before doing anything else, let's free the memory for that security descriptor.
	::FreeTransientObjectSecurityDescriptor(psd);
	// All right, time to start listening for incoming calls.
	// This function will block until the server *stops* listening.
	status = ::RpcServerListen(
		0x1,								// MSDN says specify "one" here, rumtime handles it
		RPC_C_LISTEN_MAX_CALLS_DEFAULT,		// Don't care about this limit; use the default
		TRUE);								// Return immediately (may wait later)
	if (status)
	{
		// Not able to listen for some reason... RPC error? Auto-listening?
		LogError(L"RpcServerListen failed with status %d! Cleaning up and exiting.\n", status);
		// Unregister before exiting, although it shouldn't be needed.
		WINERR err = StopRpcServer();
		LogInfo(L"Return value of StopRpcServer: %i.\n", err);
		return status;
	}
	else
	{
		LogDebug(L"RpcServerListen(1, MAX_CALLS_DEFAULT, TRUE) succeeded.\n");
	}
	LogVerbose(L"StartRpcServer complete; RPC Server registered and listening.\n");
	return status;
}

WINERR StopRpcServer(BOOL WaitForCalls)
{
	RPC_STATUS status;
	LogDebug(L"StopRpcServer(%d) called.\n", WaitForCalls);
	// First thing to do is stop listening.
	// This call would have no effect on auto-listen interfaces.
	status = ::RpcMgmtStopServerListening(nullptr);
	if (status)
	{
		// Log it, but it's not a huge deal if this fails.
		LogWarning(L"RpcMgmtStopServerListening failed with status %d!\n", status);
	}
	// Server has (probably) stopped listening; time to tear down, clean up, and exit.
	LogDebug(L"RPC server has stopped listening and is shutting down.\n");
	// Un-register the interface, but let in-progress calls complete.
	status = ::RpcServerUnregisterIf(
		nullptr,							// Remove EPVs for this UUID (all of them)
		nullptr,							// Remove EPVs for this interface (all of them)
		WaitForCalls);						// Wait until all active RPC calls complete
	if (status)
	{
		// Odd that it should fail to *un*register, especially when not giving specifics.
		LogWarning(L"RpcServerUnregisterIf failed with status %d!\n", status);
	}
	else
	{
		LogInfo(L"StopRpcServer complete, RPC server not listening and unregistered.\n");
	}
	return status;
}

// Security check function. Called after client connects. May be called more than once per client.
// Currently, just passes everything that reaches it. Need to tighten this down.
// TODO: Make the security callback actually check for authorized callers!
RPC_STATUS CALLBACK SecurityCallback(RPC_IF_HANDLE iface, PVOID context)
{
	::LogInfo(L"SecurityCallback invoked. Permitting access.\n");
	return RPC_S_OK;
}

void __RPC_USER HANDLE_to_xmit(HANDLE * h, RPC_PTR ** xmit)
{
	DWORD pid, flags;
	RPC_STATUS rstat;
	HANDLE process, result;
	// The RPC runtime doesn't allocate the transmission buffer for us. We have to do that.
	LogDebug(L"Allocating memory to transmit.\n");
	*xmit = (RPC_PTR *)midl_user_allocate(sizeof(RPC_PTR));
	// Check whether there's an actual HANDLE here...
	if (!*h || INVALID_HANDLE_VALUE == *h)
	{
		// These values have the same meaning in all processes
		LogDebug(L"HANDLE was not opened (0x%p). No need to duplicate.\n", *h);
		**xmit = reinterpret_cast<RPC_PTR>(*h);
		return;
	}
	// OK, this is a real HANDLE. Let's see if it's marked inheritable.
	if (!::GetHandleInformation(*h, &flags))
	{
		// For some reason, we couldn't get info on the supplied HANDLE.
		LogWarning(L"GetHandleInformation failed! Win32 error code %d.\n", ::GetLastError());
		// Default to marking the HANDLE inheritable
		flags = HANDLE_FLAG_INHERIT;
	}
	// We are only interested in the HANDLE_FLAG_INHERIT mark
	flags &= HANDLE_FLAG_INHERIT;
	// Now that we have the HANDLE figured out, figure out who the client is.
	LogDebug(L"Getting client process ID... \n");
	// Get the PID of the local RPC client for this thread right now.
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Not able to get the PID. Shouldn't happen for NCALRPC when processing a request...
		LogWarning(L"I_RpcBindingInqLocalClientPID failed, RPC status code %d!\n", rstat);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Cool, got the PID. Now open the process for HANDLE duplication.
	LogDebug(L"Opening client process (PID %d) to transfer HANDLE.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Opening the process failed for some reason. Unfortunately, can't return error code.
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// We've got the HANDLE to dupe and the target process HANDLE. Let's do this.
	LogDebug(L"Duplicating HANDLE into client process... \n");
	if (!::DuplicateHandle(
		GetCurrentProcess(),				// Duplicate from this process
		*h,									// Duplicate this handle
		process,							// Duplicate into this process
		&result,							// Place the value of the duplicated HANDLE here
		GENERIC_ALL,						// Irrelevant, because we are preserving access
		flags,								// Duplicate the local HANDLE's inheritability
		DUPLICATE_SAME_ACCESS))				// Duplicate with same access that original HANDLE has
	{
		// Unable to duplicate the HANDLE into the client process. Why not?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Getting here means we've successfully duped the HANDLE and are ready to return.
	LogVerbose(L"HANDLE duplicated (duplicate value: 0x%p) to client process.\n", result);
	// Don't forget to close the HANDLE to the client process!
	::CloseHandle(process);
	**xmit = (RPC_PTR)result;
}

void __RPC_USER HANDLE_from_xmit(RPC_PTR * xmit, HANDLE * h)
{
	DWORD pid;
	RPC_STATUS rstat;
	HANDLE process, source = reinterpret_cast<HANDLE>(*xmit);
	// Always assign a "safe" (meaningless) default value.
	*h = INVALID_HANDLE_VALUE;
	// Sometimes, the client sends us an open HANDLE. This code handles that.
	// The server needs to do the DuplicateHandle call; the client can't open the server process.
	// First step is to check whether this HANDLE is real...
	if (!source || INVALID_HANDLE_VALUE == source)
	{
		// This isn't a real HANDLE! Its value is the same in all processes.
		LogDebug(L"Client HANDLE is not valid (0x%p). No need to duplicate.\n", source);
		*h = source;
		return;
	}
	// Fine, we need to actually duplicate the HANDLE across. First step: identify the client.
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Failed to get the client PID! Are they not local, or something?
		LogError(L"I_RpcBindingInqLocalClientPID failed! RPC status code %d.\n", rstat);
		return;
	}
	// We have the PID now, so open the client process.
	LogDebug(L"Opening client process (PID %d) to obtain HANDLE.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Failed to open a HANDLE to the client process! Did it die suddenly?
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		return;
	}
	// Cool, process is open. Now we can duplicate from it, to us.
	// TODO: Figure out how to only mark the copied HANDLE as inheritable if it is originally!
	LogDebug(L"Duplicating HANDLE from client process... \n");
	if (!::DuplicateHandle(
		process,							// Source process (client)
		source,								// Value of the source HANDLE (in client)
		GetCurrentProcess(),				// Destination process is ourself
		h,									// Place the duplicated HANDLE's value in h
		GENERIC_ALL,						// Irrelevant, because we are preserving the access
		TRUE,								// For now, duplicated HANDLE is always inheritable
		DUPLICATE_SAME_ACCESS))				// Make the duplicated HANDLE have the original access
	{
		// Unclear why this call would fail. Bad HANDLE?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		return;
	}
	// OK, server has the HANDLE now.
	LogVerbose(L"HANDLE copied to server at value 0x%p.\n", h);
	// Don't forget to close the HANDLE to the client process.
	::CloseHandle(process);
}

void __RPC_USER HANDLE_free_inst(HANDLE * h)
{
	LogDebug(L"Closing the HANDLE (0x%p) on the server.\n", *h);
	::CloseHandle(*h);
}

void __RPC_USER HANDLE_free_xmit(RPC_PTR * xmit)
{
	LogDebug(L"Freeing the memory for the transmitted data.\n");
	midl_user_free(xmit);
}

void __RPC_USER HKEY_to_xmit(HKEY * h, RPC_PTR ** xmit)
{
	DWORD pid, flags;
	RPC_STATUS rstat;
	HANDLE process, result;
	// The RPC runtime doesn't allocate the transmission buffer for us. We have to do that.
	LogDebug(L"Allocating memory to transmit.\n");
	*xmit = (RPC_PTR *)midl_user_allocate(sizeof(RPC_PTR));
	// First check whether there's a real HKEY here...
	if (!*h || INVALID_HANDLE_VALUE == (HANDLE)(*h) || HKEY_CLASSES_ROOT == *h || 
		HKEY_CURRENT_USER == *h || HKEY_LOCAL_MACHINE == *h || HKEY_USERS == *h ||
		HKEY_PERFORMANCE_DATA == *h || HKEY_CURRENT_CONFIG == *h || HKEY_DYN_DATA == *h ||
		HKEY_CURRENT_USER_LOCAL_SETTINGS == *h || HKEY_PERFORMANCE_TEXT == *h ||
		HKEY_PERFORMANCE_NLSTEXT == *h)
	{
		// These values have the same meaning in all processes. Well, the "CURRENT USER" ones will
		// differ across processes in what they mean, but we can still just transmit them as-is.
		LogDebug(L"HKEY was not opened (0x%p). No need to duplicate.\n", *h);
		**xmit = reinterpret_cast<RPC_PTR>(*h);
		return;
	}
	// OK, this is a real HKEY. Let's see if it's marked inheritable.
	if (!::GetHandleInformation(*h, &flags))
	{
		// For some reason, we couldn't get info on the supplied HKEY.
		LogWarning(L"GetHandleInformation failed! Win32 error code %d.\n", ::GetLastError());
		// Default to marking the HKEY inheritable
		flags = HANDLE_FLAG_INHERIT;
	}
	// We are only interested in the HANDLE_FLAG_INHERIT mark
	flags = !!(flags & HANDLE_FLAG_INHERIT);
	// Now that we have the HKEY figured out, figure out who the client is.
	LogDebug(L"Getting client process ID... \n");
	// Get the PID of the local RPC client for this thread right now.
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Not able to get the PID. Shouldn't happen for NCALRPC when processing a request...
		LogWarning(L"I_RpcBindingInqLocalClientPID failed, RPC status code %d!\n", rstat);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Cool, got the PID. Now open the process for HKEY duplication.
	LogDebug(L"Opening client process (PID %d) to transfer HKEY.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Opening the process failed for some reason. Unfortunately, can't return error code.
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// We've got the HKEY to dupe and the target process HANDLE. Let's do this.
	LogDebug(L"Duplicating HKEY into client process... \n");
	if (!::DuplicateHandle(
		GetCurrentProcess(),				// Duplicate from this process
		*h,									// Duplicate this handle
		process,							// Duplicate into this process
		&result,							// Place the value of the duplicated HKEY here
		GENERIC_ALL,						// Irrelevant, because we are preserving access
		flags,								// Duplicate the local HKEY's inheritability
		DUPLICATE_SAME_ACCESS))				// Duplicate with same access that original HKEY has
	{
		// Unable to duplicate the HKEY into the client process. Why not?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Getting here means we've successfully duped the HKEY and are ready to return.
	LogDebug(L"HKEY duplicated (duplicate value: 0x%p) to client process.\n", result);
	// Don't forget to close the HANDLE to the client process!
	::CloseHandle(process);
	**xmit = (RPC_PTR)result;
}

void __RPC_USER HKEY_from_xmit(RPC_PTR * xmit, HKEY * h)
{
	// Sometimes, the client sends us an open HKEY. This code handles that.
	DWORD pid;
	RPC_STATUS rstat;
	HANDLE process, source = reinterpret_cast<HANDLE>(*xmit);
	// Always assign a "safe" (meaningless) default value.
	*h = NULL;
	// The server needs to do the DuplicateHandle call; the client can't open the server process.
	// First step is to check whether this HANDLE is real...
	if (!source || INVALID_HANDLE_VALUE == source || HKEY_CLASSES_ROOT == (HKEY)source ||
		HKEY_CURRENT_USER == (HKEY)source || HKEY_LOCAL_MACHINE == (HKEY)source ||
		HKEY_USERS == (HKEY)source || HKEY_PERFORMANCE_DATA == (HKEY)source ||
		HKEY_CURRENT_CONFIG == (HKEY)source || HKEY_DYN_DATA == (HKEY)source ||
		HKEY_CURRENT_USER_LOCAL_SETTINGS == (HKEY)source ||
		HKEY_PERFORMANCE_TEXT == (HKEY)source || HKEY_PERFORMANCE_NLSTEXT == (HKEY)source)
	{
		// This isn't an opened HKEY! Its value is the same in all processes.
		LogDebug(L"Client HKEY is a pre-defined constant (0x%p). No need to duplicate.\n",
			source);
		*h = (HKEY)source;
		return;
	}
	// Fine, we need to actually duplicate the HKEY across. First step: identify the client.
	LogDebug(L"Getting client process ID... \n");
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Failed to get the client PID! Are they not local, or something?
		LogWarning(L"I_RpcBindingInqLocalClientPID failed! RPC status code %d.\n", rstat);
		return;
	}
	// We have the PID now, so open the client process.
	LogDebug(L"Opening client process (PID %d) to obtain HKEY.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Failed to open a HANDLE to the client process! Did it die suddenly?
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		return;
	}
	// Cool, process is open. Now we can duplicate from it, to us.
	// TODO: Figure out how to only mark the copied HKEY as inheritable if it is originally!
	LogDebug(L"Duplicating HKEY from client process... \n");
	if (!::DuplicateHandle(
		process,							// Source process (client)
		source,								// Value of the source HKEY (in client)
		GetCurrentProcess(),				// Destination process is ourself
		(PHANDLE)h,							// Place the duplicated HKEY's value in h
		GENERIC_ALL,						// Irrelevant, because we are preserving the access
		TRUE,								// For now, duplicated HKEY is always inheritable
		DUPLICATE_SAME_ACCESS))				// Make the duplicated HKEY have the original access
	{
		// Unclear why this call would fail. Bad HKEY? Client process died?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		return;
	}
	// OK, server has the HKEY now.
	LogDebug(L"HKEY copied to server at value 0x%p.\n", h);
	// Don't forget to close the HANDLE to the client process.
	::CloseHandle(process);
}

void __RPC_USER HKEY_free_inst(HKEY * h)
{
	LogDebug(L"Closing the HKEY (0x%p) on the server.\n", *h);
	::RegCloseKey(*h);
}

void __RPC_USER HKEY_free_xmit(RPC_PTR * xmit)
{
	LogDebug(L"Freeing the memory for the transmitted data.\n");
	midl_user_free(xmit);
}

void __RPC_USER CHANGEHANDLE_to_xmit(CHANGEHANDLE * h, RPC_PTR ** xmit)
{
	DWORD pid, flags = 0x0;
	RPC_STATUS rstat;
	HANDLE process, result;
	// The RPC runtime doesn't allocate the transmission buffer for us. We have to do that.
	LogDebug(L"Allocating memory to transmit.\n");
	*xmit = (RPC_PTR *)midl_user_allocate(sizeof(RPC_PTR));
	// Check whether there's an actual HANDLE here...
	if (!*h || INVALID_HANDLE_VALUE == *h)
	{
		// These values have the same meaning in all processes
		LogDebug(L"HANDLE was not opened (0x%p). No need to duplicate.\n", *h);
		**xmit = reinterpret_cast<RPC_PTR>(*h);
		return;
	}
	// Now that we have the HANDLE figured out, figure out who the client is.
	LogDebug(L"Getting client process ID... \n");
	// Get the PID of the local RPC client for this thread right now.
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Not able to get the PID. Shouldn't happen for NCALRPC when processing a request...
		LogDebug(L"I_RpcBindingInqLocalClientPID failed, RPC status code %d!\n", rstat);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Cool, got the PID. Now open the process for HANDLE duplication.
	LogDebug(L"Opening client process (PID %d) to transfer HANDLE.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Opening the process failed for some reason. Unfortunately, can't return error code.
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// We've got the HANDLE to dupe and the target process HANDLE. Let's do this.
	LogDebug(L"Duplicating HANDLE into client process... \n");
	if (!::DuplicateHandle(
		GetCurrentProcess(),				// Duplicate from this process
		*h,									// Duplicate this handle
		process,							// Duplicate into this process
		&result,							// Place the value of the duplicated HANDLE here
		GENERIC_ALL,						// Irrelevant, because we are preserving access
		FALSE,								// Duplicated handle not inheritable
		DUPLICATE_SAME_ACCESS))				// Duplicate with same access that original HANDLE has
	{
		// Unable to duplicate the HANDLE into the client process. Why not?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		**xmit = (RPC_PTR)INVALID_HANDLE_VALUE;
		return;
	}
	// Getting here means we've successfully duped the HANDLE and are ready to return.
	LogVerbose(L"HANDLE duplicated (duplicate value: 0x%p) to client process.\n", result);
	// Don't forget to close the HANDLE to the client process!
	::CloseHandle(process);
	**xmit = (RPC_PTR)result;
}

void __RPC_USER CHANGEHANDLE_from_xmit(RPC_PTR * xmit, CHANGEHANDLE * h)
{
	DWORD pid;
	RPC_STATUS rstat;
	HANDLE process, source = reinterpret_cast<HANDLE>(*xmit);
	// Always assign a "safe" (meaningless) default value.
	*h = INVALID_HANDLE_VALUE;
	// Sometimes, the client sends us an open HANDLE. This code handles that.
	// The server needs to do the DuplicateHandle call; the client can't open the server process.
	// First step is to check whether this HANDLE is real...
	if (!source || INVALID_HANDLE_VALUE == source)
	{
		// This isn't a real HANDLE! Its value is the same in all processes.
		LogDebug(L"Client HANDLE is not valid (0x%p). No need to duplicate.\n", source);
		*h = source;
		return;
	}
	// Fine, we need to actually duplicate the HANDLE across. First step: identify the client.
	LogDebug(L"Getting client process ID... \n");
	rstat = ::I_RpcBindingInqLocalClientPID(NULL, &pid);
	if (rstat)
	{
		// Failed to get the client PID! Are they not local, or something?
		LogError(L"I_RpcBindingInqLocalClientPID failed! RPC status code %d.\n", rstat);
		return;
	}
	// We have the PID now, so open the client process.
	LogDebug(L"Opening client process (PID %d) to obtain HANDLE.\n", pid);
	process = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid);
	if (!process || INVALID_HANDLE_VALUE == process)
	{
		// Failed to open a HANDLE to the client process! Did it die suddenly?
		LogError(L"OpenProcess failed, return value 0x%p, Win32 error code %d!\n",
			process, ::GetLastError());
		return;
	}
	// Cool, process is open. Now we can duplicate from it, to us.
	// TODO: Figure out how to only mark the copied HANDLE as inheritable if it is originally!
	LogDebug(L"Duplicating HANDLE from client process... \n");
	if (!::DuplicateHandle(
		process,							// Source process (client)
		source,								// Value of the source HANDLE (in client)
		GetCurrentProcess(),				// Destination process is ourself
		h,									// Place the duplicated HANDLE's value in h
		GENERIC_ALL,						// Irrelevant, because we are preserving the access
		TRUE,								// For now, duplicated HANDLE is always inheritable
		DUPLICATE_SAME_ACCESS))				// Make the duplicated HANDLE have the original access
	{
		// Unclear why this call would fail. Bad HANDLE?
		LogError(L"DuplicateHandle failed! Win32 error code %d.\n", ::GetLastError());
		::CloseHandle(process);
		return;
	}
	// OK, server has the HANDLE now.
	LogVerbose(L"HANDLE copied to server at value 0x%p.\n", h);
	// Don't forget to close the HANDLE to the client process.
	::CloseHandle(process);
}

void __RPC_USER CHANGEHANDLE_free_inst(CHANGEHANDLE * h)
{
	LogDebug(L"Closing the HANDLE (0x%p) on the server.\n", *h);
	::FindCloseChangeNotification(*h);
}

void __RPC_USER CHANGEHANDLE_free_xmit(RPC_PTR * xmit)
{
	LogDebug(L"Freeing the memory for the transmitted data.\n");
	midl_user_free(xmit);
}

void __RPC_USER FINDHANDLE_rundown(FINDHANDLE fh)
{
	LogWarning(L"Client vanished with an open Find*File handle; closing it.\n");
	RootRpcFindClose(&fh);
}

void __RPC_USER FINDVOLHANDLE_rundown(FINDVOLHANDLE fvh)
{
	LogWarning(L"Client vanished with an open Find*Volume handle; closing it.\n");
	RootRpcFindVolumeClose(&fvh);
}

void __RPC_USER SERVICEHANDLE_rundown(SERVICEHANDLE sh)
{
	LogWarning(L"Client vanished with an open ServiceHandle (%p); closing it.\n", sh);
	RootRpcCloseServiceHandle(&sh);
}

// Called if a client took a reference and then exited without releasing it.
// Allows the server to shut down cleanly when its last client disconnects.
void __RPC_USER ROOTRPC_REFERENCE_rundown(ROOTRPC_REFERENCE reference)
{
	LogWarning(L"Client (PID %d) vanished without closing reference context.\n", reference);
	RootRpcRelease(&reference);
}

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
	return(malloc(len));
}

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
	free(ptr);
}

DWORD RootRpcServerVersion(PDWORD major, PDWORD minor, PDWORD bugfix)
{
	if (major) *major = VER_MAJOR;
	if (minor) *minor = VER_MINOR;
	if (bugfix) *bugfix = VER_UPDATE;
	return VER_BUILD;
}

WINERR RootRpcInit(
	/* [out] */ ROOTRPC_REFERENCE *reference)
{
	WINERR err;
	// Update the count.
	DWORD count = InterlockedIncrement(&clientCount);
	// Store the client PID as the context handle value.
	err = ::I_RpcBindingInqLocalClientPID(nullptr, reinterpret_cast<PDWORD>(reference));
	if (err)
	{
		LogError(L"I_RpcBindingInqLocalClientPID in RootRpcInit failed with status %d!\n", err);
	}
	else
	{
		LogInfo(
			L"Reference created for client PID %d, which is client number %d.\n",
			*reinterpret_cast<PDWORD>(reference),
			count);
	}
	return err;
}

// Required as an entry point for CreateThread. Lets the server shut down asynchronously.
DWORD WINAPI StopServerAsync(PVOID p)
{
	_CRT_UNUSED(p);
	// The server process will terminate quickly after the service stops.
	LogVerbose(L"Shutting service down asynchronously.\n");
	return ServiceStop();
}

WINERR RootRpcRelease(
/* [out][in] */ ROOTRPC_REFERENCE *reference)
{
	WINERR err = NO_ERROR;
	DWORD pid = *reinterpret_cast<PDWORD>(reference);
	*reference = nullptr;
	DWORD count = InterlockedDecrement(&clientCount);
	if (!count)
	{
		// That was the last client. Create a thread to stop the server.
		// Can't do it in this thread because server waits for calls to finish before stopping.
		if (!::CreateThread(nullptr, 0, StopServerAsync, nullptr, 0x0, nullptr))
		{
			// The thread creation failed for some reason!
			err = ::GetLastError();
			LogError(L"CreateThread for StopServerAsync failed with error %d!\n", err);
			// Just stop the server now. Stuff may break. Not our problem anymore.
			ServiceStop(err);
			return err;
		}
		LogInfo(L"Reference released from last client, PID %d. Server stopping.\n", pid);
	}
	else
	{
		LogInfo(L"Reference released from client PID %d. Clients remaining: %d.\n", pid, count);
	}
	return err;
}

WINERR RootRpcGetServerProcess(
	/* [out] */ PHANDLE result)
{
	*result = ::OpenProcess(PROCESS_ALL_ACCESS, TRUE, ::GetCurrentProcessId());
	if (*result)
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}

WINERR RootRpcGetServerToken(
	/* [out] */ PHANDLE result)
{
	if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, result))
	{
		return NO_ERROR;
	}
	return ::GetLastError();
}